package client

import (
	"context"
	"distributed_db/utils"
	"fmt"
	"strings"
	"sync"
	"time"
)

// Request 定义请求结构
type Request struct {
	Type string      `json:"type"`
	Data RequestData `json:"data"`
}

// TODO: 解耦Response，每一个操作都有对应的response
// Response 定义响应结构
type Response struct {
	Status        string                   `json:"status"`
	Data          []map[string]interface{} `json:"data"`
	LeaseID       int64                    `json:"lease_id,omitempty"`
	TTL           int64                    `json:"ttl,omitempty"`
	RegionServers map[string][]string      `json:"region_servers"`
}

// Client 客户端结构
type Client struct {
	clerk         *Clerk
	cache         map[string]Response // 缓存 key -> Response
	cacheTTL      int                 // 缓存的生存时间
	mu            sync.Mutex
	ctx           context.Context
	cancel        context.CancelFunc
	RegionServers map[string]map[string]int // 缓存的 RegionServer 地址列表，table_name对多个regionserver的ip
}

// NewClient 创建新客户端，返回一个 Client 对象
func NewClient(masterAddr []string) (*Client, error) {
	conn := make([]*utils.ClientEnd, 0)
	for _, addr := range masterAddr {
		end := utils.MakeEnd(addr)
		if end == nil {
			return nil, fmt.Errorf("连接 %s 失败", addr)
		}
		conn = append(conn, end)
	}

	ctx, cancel := context.WithCancel(context.Background())
	client := &Client{
		clerk:         MakeClerk(conn),
		cache:         make(map[string]Response),
		ctx:           ctx,
		cancel:        cancel,
		RegionServers: make(map[string]map[string]int),
		cacheTTL:      45,
	}

	// 启动 KeepAlive 协程
	go client.keepAlive()

	return client, nil
}

// 封装更新缓存
func (c *Client) updateCache(ttl int64, region_servers map[string]map[string]int) {
	// 更新缓存
	c.mu.Lock()
	c.cacheTTL = int(ttl) + 45
	for k, v := range region_servers {
		c.RegionServers[k] = v
	}
	fmt.Printf("更新缓存: %v\n及其租期: %d\n", region_servers, ttl)
	c.mu.Unlock()
}

func (c *Client) FlushCache() {
	c.mu.Lock()
	c.RegionServers = make(map[string]map[string]int)
	c.cache = make(map[string]Response)
	c.mu.Unlock()
}

// SendRequest 发送查询请求
// 预处理，检查tablename是否在缓存中，如果命中，则直接查询对应的regionserver，否则更新缓存，并查询对应的regionserver
func (c *Client) Select(query string) (Response, error) {
	req, err := c.constructRequest(query)
	if err != nil {
		return Response{}, fmt.Errorf("请求%s构造失败: %v", query, err)
	}

	// 检查缓存是否命中 RegionServer 列表
	result := make([]map[string]interface{}, 0)
	if ips, ok := c.RegionServers[req.Data.TableName]; ok {
		// 缓存命中，轮询，直到结束
		if len(ips) > 0 {
			for ip := range ips {
				utils.LogC(utils.Green, "命中缓存，查询 %s\n", ip)
				resp, err := c.selectRegionServer(ip, req)
				if err != nil {
					utils.LogC(utils.Red, "查询失败: %v", err)
					continue
				} else {
					result = append(result, resp.Data...)
				}
			}
		}
		return Response{Status: "ok", Data: result}, nil
	} else {
		resp := c.clerk.UpdateCache([]string{req.Data.TableName})
		if resp.Err != "OK" {
			utils.LogC(utils.Red, "缓存未命中，更新缓存失败: %s\n", resp.Err)
			return Response{}, fmt.Errorf("缓存未命中并且更新缓存失败，请检查table名是否正确")
		}
		// 更新缓存
		c.mu.Lock()
		c.RegionServers[req.Data.TableName] = resp.RegionServers[req.Data.TableName]
		c.mu.Unlock()
		utils.LogC(utils.Green, "虽然查询失败，但是更新缓存成功，将再次尝试查询\n")
		return c.Select(query)

	}
}

func (c *Client) CreateTable(query string) (Request, error) {
	req, err := c.constructRequest(query)
	if err != nil {
		return Request{}, fmt.Errorf("请求%s构造失败: %v", query, err)
	}
	resp := c.clerk.CreateTable(req.Data.TableName, req.Data.Cols)
	if resp.Err != "OK" {
		return Request{}, fmt.Errorf("创建表失败: %s", resp.Err)
	}
	return req, nil
}

func (c *Client) DropTable(query string) (Request, error) {
	req, err := c.constructRequest(query)
	if err != nil {
		return Request{}, fmt.Errorf("请求%s构造失败: %v", query, err)
	}
	resp := c.clerk.DropTable(req.Data.TableName)
	if resp.Err != "OK" {
		return Request{}, fmt.Errorf("删除表失败: %s", resp.Err)
	}
	return req, nil
}

func (c *Client) InsertData(query string) (Request, error) {
	req, err := c.constructRequest(query)
	if err != nil {
		return Request{}, fmt.Errorf("请求%s构造失败: %v", query, err)
	}
	resp := c.clerk.InsertData(req.Data.TableName, req.Data.Cols)
	if resp.Err != "OK" {
		return Request{}, fmt.Errorf("插入数据失败: %s", resp.Err)
	}
	return req, nil
}

func (c *Client) DeleteData(query string) (Request, error) {
	req, err := c.constructRequest(query)
	if err != nil {
		return Request{}, fmt.Errorf("请求%s构造失败: %v", query, err)
	}
	resp := c.clerk.DeleteData(req.Data.TableName, req.Data.Condition)
	if resp.Err != "OK" {
		return Request{}, fmt.Errorf("删除数据失败: %s", resp.Err)
	}
	return req, nil
}

func (c *Client) Keep(query string) (Request, error) {
	req, err := c.constructRequest(query)
	if err != nil {
		return Request{}, fmt.Errorf("请求%s构造失败: %v", query, err)
	}
	resp := c.clerk.UpdateCache(req.Data.TableNames)
	c.updateCache(resp.TTL, resp.RegionServers)
	if resp.Err != "OK" {
		return Request{}, fmt.Errorf("更新缓存失败: %s", resp.Err)
	}
	return req, nil
}

// // sendToMaster 向 Master 发送请求
// func (c *Client) sendToMaster(req Request) (Response, error) {
// 	data, err := json.Marshal(req)
// 	if err != nil {
// 		return Response{}, fmt.Errorf("请求编码失败: %v", err)
// 	}
// 	resp := Response{}
// 	var b bool
// 	switch req.Type {
// 	case "insert":
// 		b = c.conn.Call("Master.InsertData", data, &resp)
// 	case "delete":
// 		b = c.conn.Call("Master.DeleteData", data, &resp)
// 	case "update":
// 		b = c.conn.Call("Master.UpdateData", data, &resp)
// 	case "keepalive":
// 		b = c.conn.Call("Master.UpdateRegionServers", data, &resp)
// 		err = nil
// 	default:
// 		err = fmt.Errorf("请求类型%s不支持", req.Type)
// 	}
// 	if err != nil || !b {
// 		return Response{}, fmt.Errorf("发送请求失败: %v", err)
// 	}
// 	return resp, nil
// }

// sendToRegionServer 向 RegionServer 发送请求
func (c *Client) selectRegionServer(rsAddr string, req Request) (Response, error) {
	var rep utils.QueryRegionReply
	// 测试时使用本地连接，正式环境使用rpc连接
	cols := make([]string, 0)
	for k := range req.Data.Cols {
		cols = append(cols, k)
	}
	args := utils.QueryRegionArgs{
		TxID:  "",
		Table: req.Data.TableName,
		Row:   strings.Join(cols, ","),
		Cond:  req.Data.Condition,
	}
	conn := utils.MakeEnd(rsAddr)
	if conn == nil {
		return Response{}, fmt.Errorf("连接 RegionServer %s 失败", rsAddr)
	}
	b := conn.Call("RegionServerConnection.QueryRegion", &args, &rep)
	if !b {
		return Response{}, fmt.Errorf("查询失败")
	}
	resp := Response{Status: "ok", Data: rep.Data}
	return resp, nil
	// ts := strings.Split(rsAddr, ":")
	// ip := ts[0]
	// port := ts[1]
	// dbc := db.NewDBConnection(ip, port, "root", "130900zhang", daName)
	// if dbc == nil {
	// 	return Response{}, fmt.Errorf("连接 RegionServer %s 失败", rsAddr)
	// }
	// cols := make([]string, 0)
	// for k := range req.Data.Cols {
	// 	cols = append(cols, k)
	// }
	// result, err := dbc.QueryRegion(nil, req.Data.TableName+"_region_1", strings.Join(cols, ","), req.Data.Condition)
	// if err != nil {
	// 	return Response{}, fmt.Errorf("查询失败: %v", err)
	// }
	// return Response{Status: "ok", Data: result}, nil
}

// keepAlive 维护 Master 连接活跃
func (c *Client) keepAlive() {
	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()
	retry := 5
	for {
		select {
		case <-ticker.C:
			// 确保 KeepAlive 响应更新缓存计时器
			if c.cacheTTL == 45 {
				fmt.Println("KeepAlive 响应更新缓存计时器")
				tables := make([]string, 0)
				// 发送 KeepAlive 请求，遍历缓存，包含缓存的所有 regionserver 列表
				for k := range c.RegionServers {
					tables = append(tables, k)
				}
				resp := c.clerk.UpdateCache(tables)
				if resp.Err != "OK" {
					retry--
					if retry == 0 {
						utils.LogC(utils.Red, "KeepAlive 失败，超过最大重试次数: %s\n", resp.Err)
						return
					}
					utils.LogC(utils.Red, "KeepAlive 失败，等待一段时间再次尝试: %s\n", resp.Err)
					c.mu.Lock()
					c.cacheTTL += 5
					c.mu.Unlock()
					continue
				}
				fmt.Printf("KeepAlive 响应: %v\n", resp)
				// 更新缓存
				c.updateCache(resp.TTL, resp.RegionServers)
			} else if c.cacheTTL == 0 {
				fmt.Println("KeepAlive 协程退出，超过45s容忍期，或许需要更新master的ip地址")
				return
			} else {
				c.mu.Lock()
				c.cacheTTL -= 1
				c.mu.Unlock()
			}
		case <-c.ctx.Done():
			fmt.Println("KeepAlive 协程被提前终止")
			return
		}
	}
}

// Close 关闭连接
func (c *Client) Close() {
	// 通过上下文的方式，关闭keepAlive协程
	c.ctx.Done()
	c.clerk.Close()
}
